package com.cloud.login.service.impl;

import cn.hutool.crypto.SecureUtil;
import com.cloud.annex.AnnexUrlService;
import com.cloud.common.constants.GlobalConstants;
import com.cloud.common.domain.Result;
import com.cloud.domain.CheckLogin;
import com.cloud.domain.LoginUser;
import com.cloud.domain.UserProject;
import com.cloud.entity.*;
import com.cloud.enums.LoginDevice;
import com.cloud.enums.SysType;
import com.cloud.common.enums.UserAuthStatus;
import com.cloud.common.enums.UserStatus;
import com.cloud.exception.CustomException;
import com.cloud.exception.LoginException;
import com.cloud.security.service.ISDKClientService;
import com.cloud.security.service.ISecurityService;
import com.cloud.service.RedisService;
import com.cloud.system.service.ISysLoginLogsService;
import com.cloud.user_info.mapper.SysUserAuthMapper;
import com.cloud.user_info.service.ISysDeptService;
import com.cloud.user_info.service.ISysRoleService;
import com.cloud.user_info.service.ISysUserService;
import com.cloud.user_info.service.impl.SysPermissionService;
import com.cloud.utils.SecurityUtils;
import com.cloud.utils.ServletUtils;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 账户授权登录
 *
 * @author yzj
 */
@Component
public class AuthLoginService {

    @Resource
    private RedisService redisService;

    @Resource
    private AnnexUrlService annexUrlService;

    @Resource
    private SysUserAuthMapper userAuthMapper;

    @Autowired
    private ISysDeptService deptService;

    @Autowired
    private ISysRoleService sysRoleService;

    @Autowired
    private SysPermissionService permissionService;

    @Autowired
    private ISysUserService userService;

    @Autowired
    private ISysLoginLogsService logLoginService;

    @Autowired
    private ISDKClientService authSDKService;

    @Autowired
    private ISecurityService securityService;

    /**
     * 根据授权信息获取用户信息
     */
    public SysUser getUserByAuth(AuthBody authBody) {
        return userService.selectUserByAuth(authBody);
    }

    /**
     * 校验账户信息登录
     */
    public SysUser checkUserByLogin(LoginBody loginBody, String loginType) {
        CheckLogin checkLogin = checkLoginFailedNum(loginBody.getUserName(), false);
        if (checkLogin.getLastNum() <= 0) {
            throw new CustomException("登录失败:账户已锁定,请" + checkLogin.getFailTime() + "分钟后重试");
        }
        if (LoginDevice.PC.getCode().equals(loginType)) {
            if (StringUtils.isEmpty(loginBody.getCode())) {
                throw new CustomException(LoginException.CODE_IS_EMPTY.getCode(),
                        LoginException.CODE_IS_EMPTY.getMsg());
            }
            String captcha = checkRedisData(GlobalConstants.sysLoginCode + loginBody.getUuid());
            if (!loginBody.getCode().equals(captcha)) {
                throw new CustomException(LoginException.CODE_MISTAKE.getCode(),
                        LoginException.CODE_MISTAKE.getMsg());
            }
        }
        loginBody.setPassword(SecureUtil.md5(loginBody.getPassword()));
        SysUser sysUser = userService.selectUserByLogin(loginBody);
        if (ObjectUtils.isEmpty(sysUser)) {
            checkLogin = checkLoginFailedNum(loginBody.getUserName(), true);
            if (checkLogin.getLastNum() > 0) {
                throw new CustomException("登录失败:账户或密码错误," + checkLogin.getLastNum()
                        + "次之后账户锁定" + checkLogin.getFailTime() + "分钟");
            } else {
                throw new CustomException("登录失败:账户已锁定,请" + checkLogin.getFailTime() + "分钟后重试");
            }
        }
        return sysUser;
    }

    /**
     * 授权添加用户信息
     */
    public AuthUser saveAuthUserInfo(AuthBody authBody) {
        // 根据Code获取浙政钉用户信息
        AuthUser authUser = authSDKService.getDingGrantAuthInfo(authBody).getData();
        if (ObjectUtils.isEmpty(authUser)) {
            return null;
        }
        // 获取用户信息不为空,校验系统用户是否存在,反之新增授权用户信息
        authUser.setAuthType(authUser.getAuthType());
        SysUserAuth sysUserAuth = userAuthMapper.selectUserAuthByCode(authUser);
        SysUserAuth userAuth = new SysUserAuth();
        userAuth.setSysCode(authUser.getSysCode());
        if (ObjectUtils.isNotEmpty(sysUserAuth)) {
            // 判断账户授权信息是否被禁用
            if (UserAuthStatus.DEACTIVATE.getCode() == sysUserAuth.getAuthState()) {
                throw new CustomException("授权账户" + authUser.getLastName() + "已被删除或停用");
            }
            // 如果授权信息缺少账户ID,则重新设置授权参数
            if (StringUtils.isEmpty(sysUserAuth.getAccountId())) {
                userAuth.setAuthId(sysUserAuth.getAuthId());
                userAuth.setAccountId(authUser.getAccountId());
                userAuth.setAuthState(UserAuthStatus.ENABLE.getCode());
                userAuthMapper.updateById(userAuth);
            }
        } else {
            SysUser user = userService.selectUserByName(authUser.getAccount());
            if (ObjectUtils.isEmpty(user)) {
                user = new SysUser();
                user.setUserName(authUser.getAccount());
                user.setNickName(authUser.getLastName());
                user.setAvatar(authUser.getGovEmpAvatar());
                user.setDeptId(authUser.getAuthDeptId());
                user.setUserType(SysType.DEFAULT_USER_TYPE.getCode());
                user.setPassword(SecureUtil.md5("123456"));
                userService.insertUserInfo(user);
            }
            // 添加系统用户授权信息
            userAuth.setUserId(user.getUserId());
            userAuth.setAuthType(authUser.getAuthType());
            userAuth.setAuthCode(authUser.getOpenid());
            userAuth.setAccountId(authUser.getAccountId());
            userAuth.setAuthState(UserAuthStatus.ENABLE.getCode());
            int rows = userAuthMapper.checkUserAuthUnique(userAuth);
            if (rows <= 0) {
                userAuthMapper.insert(userAuth);
            }
        }
        return authUser;
    }

    /**
     * 设置用户/关联项目权限Token信息
     */
    public Result setLoginPointToken(String sysCode) {
        HttpServletRequest request = ServletUtils.getRequest();
        String loginToken = request.getHeader(GlobalConstants.tokenHeader);
        if (StringUtils.isEmpty(loginToken)) {
            return Result.error("校验失败:获取登录Token信息失败");
        }
        LoginUser loginUser = SecurityUtils.getLoginUser();
        if (ObjectUtils.isEmpty(loginUser)) {
            return Result.error("校验失败:获取账户信息失败");
        }
        loginUser.setSysCode(sysCode);
        Long userId = SecurityUtils.getUserId();
        loginUser.setUserProject(selectUserProject(userId, sysCode));
        securityService.setProjectByCode(loginUser);
        Map<String, Object> resultMap = new HashMap<>();
        SysUser userInfo = loginUser.getUser();
        if (ObjectUtils.isNotEmpty(userInfo)) {
            resultMap.put("userName", userInfo.getUserName());
            resultMap.put("nickName", userInfo.getNickName());
            resultMap.put("sex", userInfo.getSex());
            resultMap.put("phone", userInfo.getPhone());
            resultMap.put("avatar", userInfo.getAvatar());
        }
        String codeKey = getUserOpenId(sysCode, userId);
        String tokenCode = GlobalConstants.authTokenCode + codeKey;
        resultMap.put("openId", codeKey);
        redisService.set(tokenCode, resultMap, GlobalConstants.EXPIRE_TIME, TimeUnit.MINUTES);
        return Result.success("操作成功", resultMap);
    }

    /**
     * 根据授权Code获取Token信息
     */
    public Result getAuthPointToken(String openId) {
        String tokenCode = GlobalConstants.authTokenCode + openId;
        Map<String, Object> resultMap = redisService.get(tokenCode);
        return Result.success("查询成功", resultMap);
    }

    /**
     * 添加登录日志
     */
    public Result saveLoginLogs(LoginUser loginUser, SysLoginLogs loginLogs) {
        SysUser userInfo = loginUser.getUser();
        if (ObjectUtils.isEmpty(userInfo)) {
            throw new CustomException("检验失败:用户信息不能为空");
        }
        if (UserStatus.DELETED.getCode() == userInfo.getStatus()) {
            throw new CustomException(LoginException.USER_INFO_DELETED.getMsg());
        } else if (UserStatus.DISABLE.getCode() == userInfo.getStatus()) {
            throw new CustomException(LoginException.USER_INFO_DISABLE.getMsg());
        }
        loginUser.setLoginDevice(loginLogs.getLoginDevice());
        userInfo.setAvatar(annexUrlService.getImageOrVideoPath(userInfo.getAvatar(), loginUser.getLoginDevice()));
        // 查询角色所在的部门信息
        userInfo.setDept(deptService.selectDeptById(userInfo.getDeptId()));
        // 查询部门以下数据权限(用于数据权限拦截)
        loginUser.setDeptAfterScope(deptService.selectDeptAfterScope(userInfo.getDeptId()));
        // 查询所在项目权限信息
        loginUser.setUserProject(selectUserProject(userInfo.getUserId(), loginUser.getSysCode()));
        loginUser.setIpAddr(ServletUtils.getClientIPAddress(ServletUtils.getRequest()));
        if (userInfo.getUserName().equals(userInfo.getNickName())) {
            loginUser.setNickName(userInfo.getUserName());
        } else {
            loginUser.setNickName(userInfo.getUserName() + "/" + userInfo.getNickName());
        }
        loginUser.setUser(userInfo);
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("token", securityService.createToken(loginUser));
        if (ObjectUtils.isNotEmpty(resultMap)) {
            resultMap.put("userName", userInfo.getUserName());
            resultMap.put("nickName", userInfo.getNickName());
            resultMap.put("sex", userInfo.getSex());
            resultMap.put("phone", userInfo.getPhone());
            resultMap.put("avatar", userInfo.getAvatar());
            String codeKey = getUserOpenId(loginLogs.getSysCode(), userInfo.getUserId());
            String tokenCode = GlobalConstants.authTokenCode + codeKey;
            resultMap.put("openId", codeKey);
            redisService.set(tokenCode, resultMap, GlobalConstants.EXPIRE_TIME, TimeUnit.MINUTES);
            loginLogs.setLoginStatus(Result.SUCCESS);
        } else {
            loginLogs.setLoginStatus(Result.ERROR);
        }
        loginLogs.setLoginDevice(loginLogs.getLoginDevice());
        loginLogs.setLoginMsg("操作成功");
        loginLogs.setLoginTime(new Date());
        logLoginService.insert(loginLogs);
        return Result.success("操作成功", resultMap);
    }

    /**
     * 查询用户项目/权限信息
     */
    private UserProject selectUserProject(Long userId, String sysCode) {
        UserProject userProject = new UserProject();
        // 获取关联项目角色信息
        List<SysRole> roleList = sysRoleService.selectRoleListById(sysCode, userId);
        userProject.setRoleList(roleList);
        // 获取账户角色权限列表
        Set<String> permissions = new HashSet<>();
        if (ObjectUtils.isNotEmpty(roleList)) {
            permissions = permissionService.getMenuPermission(sysCode, userId);
        }
        userProject.setPermissions(permissions);
        Map<Long, String> roleDeptMap = new HashMap<>();
        for (SysRole role : userProject.getRoleList()) {
            // 获取角色部门数据权限(用于数据权限拦截)
            roleDeptMap.put(role.getRoleId(), deptService.selectDeptScopeByRoleId(role.getRoleId()));
        }
        userProject.setRoleDeptScope(roleDeptMap);
        return userProject;
    }

    /**
     * 校验登录失败次数
     *
     * @param userName   账户名称
     * @param isSetRedis 失败次数是否添加到缓存
     * @return
     */
    private CheckLogin checkLoginFailedNum(String userName, boolean isSetRedis) {
        // 账户登录失败次数
        int lastNum = 5;
        CheckLogin checkLogin = new CheckLogin();
        String redisKey = GlobalConstants.sysLoginNum + userName;
        Integer failedNum = redisService.get(redisKey);
        if (ObjectUtils.isNotEmpty(failedNum) && failedNum > 0) {
            int snapNum = failedNum + 1;
            if (lastNum == snapNum) {
                checkLogin.setLastNum(0);
                checkLogin.setFailTime(redisService.getKeyExpire(redisKey, TimeUnit.MINUTES));
                return checkLogin;
            }
            checkLogin.setLastNum(lastNum - snapNum);
            checkLogin.setFailTime(10L);
            if (isSetRedis) {
                redisService.set(redisKey, snapNum, 10, TimeUnit.MINUTES);
            }
            return checkLogin;
        } else {
            checkLogin.setLastNum(lastNum - 1);
            checkLogin.setFailTime(10L);
            if (isSetRedis) {
                redisService.set(redisKey, 1, 10, TimeUnit.MINUTES);
            }
            return checkLogin;
        }
    }

    /**
     * 验证缓存中的数据
     */
    private String checkRedisData(String key) {
        String captcha = redisService.get(key);
        if (ObjectUtils.isNotEmpty(captcha)) {
            // 移除缓存中验证码信息
            redisService.del(key);
        }
        return captcha;
    }

    // 计算项目下用户唯一标识
    private String getUserOpenId(String sysCode, Long userId) {
        return SecureUtil.md5(sysCode + "/" + userId);
    }

}
